import java.math.BigDecimal;
import java.util.concurrent.*;

/**
 *
 * @author Jun
 * @date 2019/3/23
 */
public class Main {

    /**
     * 借方账户
     */
    static Account debtor;
    /**
     * 贷方账户
     */
    static Account creditor;
    /**
     * 中间账户
     */
    static Account intermediary;

    /**
     * 借方余额
     */
    static BigDecimal debitBalance = BigDecimal.ZERO;
    /**
     * 贷方余额
     */
    static BigDecimal creditBalance = BigDecimal.ZERO;
    /**
     * 借方发生额
     */
    static BigDecimal debitAmount = BigDecimal.ZERO;
    /**
     * 贷方发生额
     */
    static BigDecimal creditAmount = BigDecimal.ZERO;

    static {
        debtor = new Account("B", BigDecimal.ZERO, DirectionEnum.DEBIT.getValue());
        creditor = new Account("A", BigDecimal.valueOf(100), DirectionEnum.CREDIT.getValue());
        intermediary = new Account("C", BigDecimal.ZERO, DirectionEnum.CREDIT.getValue());
    }

    /**
     * 重新初始化账户数据
     */
    public static void rebuild() {
        debtor = new Account("B", BigDecimal.ZERO, DirectionEnum.DEBIT.getValue());
        creditor = new Account("A", BigDecimal.valueOf(1000), DirectionEnum.CREDIT.getValue());
        intermediary = new Account("C", BigDecimal.ZERO, DirectionEnum.CREDIT.getValue());
        debitBalance = BigDecimal.ZERO;
        creditBalance = BigDecimal.ZERO;
        debitAmount = BigDecimal.ZERO;
        creditAmount = BigDecimal.ZERO;
    }

    /**
     * 借贷交易
     *
     * @param a
     * @param money
     */
    public static synchronized void trans(Account a, BigDecimal money) {
        if (a.getDirection().equals(DirectionEnum.CREDIT.getValue())) {
            // 贷方余额不足
            if (!a.subMoeny(money)) {
                return;
            }
            intermediary.addMoney(money);
            synchronized (creditBalance) {
                creditBalance = creditBalance.add(money);
            }
            synchronized ((creditAmount)) {
                creditAmount = creditAmount.add(money);
            }
        } else if (a.getDirection().equals(DirectionEnum.DEBIT.getValue())) {
            // 中间账户余额不足
            if (!intermediary.subMoeny(money)) {
                return;
            }
            a.addMoney(money);
            synchronized (creditBalance) {
                creditBalance = creditBalance.subtract(money);
            }
            synchronized (debitAmount) {
                debitAmount = debitAmount.add(money);
            }
        }
    }


    public static void main(String[] args) {
        trans(creditor, BigDecimal.valueOf(100));
        trans(debtor, BigDecimal.valueOf(50));
        System.out.println("借方余额：" + debitBalance);
        System.out.println("贷方余额：" + creditBalance);
        System.out.println("借方发生额：" + debitAmount);
        System.out.println("贷方发生额：" + creditAmount);

        System.out.println("高并发----------------");
        rebuild();
        final CountDownLatch latch = new CountDownLatch(100);
        ArrayBlockingQueue workQueue = new ArrayBlockingQueue(100);
        ExecutorService fixedThreadPool = new ThreadPoolExecutor(3, 10, 5, TimeUnit.HOURS, workQueue);
        for (int i = 0; i < 100; i++) {
            fixedThreadPool.execute(new Runnable() {
                ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();
                @Override
                public void run() {
                    int a = threadLocalRandom.nextInt(100);
                    int b = threadLocalRandom.nextInt(100);
                    trans(creditor, BigDecimal.valueOf(a));
                    trans(debtor, BigDecimal.valueOf(b));
                    latch.countDown();
                }
            });
        }

        try {
            latch.await();
            fixedThreadPool.shutdown();
            System.out.println("借方余额：" + debitBalance);
            System.out.println("贷方余额：" + creditBalance);
            System.out.println("借方发生额：" + debitAmount);
            System.out.println("贷方发生额：" + creditAmount);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}
